﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading.Tasks;

namespace GMA_SERVER
{
    class Program
    {
        public static int __port = 0;
        public static string __passwd = string.Empty;
        public static string __dns = string.Empty;
        public static bool __lock = true;
        public static Socket s;

        public delegate void FileRecievedEventHandler(object source, string fileName);
        public event FileRecievedEventHandler NewFileRecieved;
        public static TcpListener newList;

        public static bool flock = false;

        static void Main(string[] args)
        {
            __Startup();
            
        }
        
        /*------------------------------*/

        public static void __Startup(){
            Console.ForegroundColor = System.ConsoleColor.Green;
            __load_config();
            
            Console.WriteLine("The server is running at {0} port {1}...", __port.ToString(), __dns);
            Console.WriteLine("Waiting for a connection.....");
        L001:
            try
            {
                IPAddress ipAd = IPAddress.Parse(__dns);
                newList = new TcpListener(ipAd, __port);
                try
                {
                    newList.Start();
                }
                catch (Exception ex)
                {
                    goto L001;
                }
                s = newList.AcceptSocket();
                NetworkStream nts = new NetworkStream(s);
                StreamReader strea = new StreamReader(nts);
                StreamWriter strwri = new StreamWriter(nts);
                Console.WriteLine("Remote accepted from " + s.RemoteEndPoint);

                byte[] b = new byte[5000];
                //byte[] b = null;
                int k = 0;
                try
                {
                    k = s.Receive(b);
                }
                catch (Exception ex)
                {
                    s.Close();
                    newList.Stop();
                    goto L001;
                }
                Console.WriteLine("Recieved...");
                StringBuilder bd = new StringBuilder();
                for (int i = 0; i < k; i++)
                {
                    bd.Append(Convert.ToChar(b[i]).ToString());
                }
                Console.WriteLine("Command => " + bd.ToString());

                Console.WriteLine("\r\nThe byte data is: ");
                for (int i = 0; i < k; i++)
                    Console.Write(b[i]);

                ASCIIEncoding asen = new ASCIIEncoding();
                //s.Send(asen.GetBytes(__Command_lib(bd.ToString())));
            
                switch (__Command_lib(bd.ToString()))
                {
                    case "file":
                        {
                            strwri.WriteLine("File OK!");
                            strwri.Flush();
                            //s.Send(asen.GetBytes("File is OK!"));
                            s.Close();
                            
                            goto L001;
                            break;
                        }
                    default:
                        s.Send(asen.GetBytes(__Command_lib(bd.ToString())));
                        break;
                }
                
                string output;
                output = strea.ReadLine();
                Console.WriteLine("\r\nReturn info => " + output);


                s.Close();
                newList.Stop();
                //goto L001;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error => " + ex.Message + Environment.NewLine);
                s.Close();
                
                goto L001;
            }
            
            goto L001;


        }

        public static void __load_config() {
            if (File.Exists("config.sys"))
            {
                string[] avdns = get_text_file("config.sys").Split(':');
                __dns = avdns[0];
                __port = Convert.ToInt32(avdns[1]);
            }
            else
            {
                __dns = GetIP();
                __port = 7777;
            }


            if (File.Exists("secrets.sys"))
            {
                __passwd = get_text_file("secrets.sys");

            }
            else
            {
                __passwd = Encrypt("NILL");
            }
        }

        public static string __Command_lib(string __cmd)
        {
            string __msg_notlogin = "Sorry, you're not login!";

            try
            {
                string[] tmp = __cmd.Split(' ');
                string cmd = tmp[0];
                string param = tmp[1];

                switch (cmd)
                {
                    case "cmd":
                        {
                            if (__login())
                            {
                                Console.WriteLine("Execute param => " + param.Replace("_", " ").Replace(":", ":\\").Replace("*", "."));
                                string __result = ExecuteCommandSync(param.Replace("_", " ").Replace(":", ":\\").Replace("*", "."));
                                return "Result =>\r\n" + __result;
                            }
                            else {
                                return __msg_notlogin;
                            }
                            
                        }
                    case "login":
                        {
                            __load_config();
                            if (Decrypt(__passwd).Trim() == param.Trim())
                            {
                                __lock = false;
                                string[] rempip = s.RemoteEndPoint.ToString().Split(':');
                                writetext("session.sys", Encrypt(rempip[0] + " " + DateTime.Now.ToString().Replace(" ", "_") + " " + __passwd));
                                
                                return "New session has been add\r\n" + rempip[0] + " " + DateTime.Now;
                            }
                            else {
                                return "Sorry, password was wrong!";
                            }
                        }
                    case "passwd":
                        {
                            
                            if (__login() && !__lock)
                            {
                                writetext("secrets.sys", Encrypt(param.Trim()));
                                __lock = true;
                                __load_config();
                                return "Password has been change!";
                            }
                            else {
                                return __msg_notlogin;
                            }
                            
                        }
                    case "logout":
                        {
                            if (__login())
                            {
                                __lock = true;
                                writetext("session.sys", string.Empty);
                                return "Server has been logout!";
                            }
                            else {
                                return __msg_notlogin;
                            }
                            
                        }
                    case "session":
                        {
                            if (__login())
                            {
                                return Decrypt(get_text_file("session.sys"));
                            }
                            else {
                                return __msg_notlogin;
                            }
                        }
                    case "proc":
                        { 
                            Process[] processlist = Process.GetProcesses();
                            string __proc = "";
                            foreach(Process theprocess in processlist){
                                __proc+= "Process: "+theprocess.ProcessName+" ID: "+ theprocess.Id+Environment.NewLine;
                            }
                            return __proc;
                        }
                    case "file":
                        {
                            if (__login())
                            {
                                if (param == "open")
                                {
                                    Task.Factory.StartNew(() => HandleIncomingFile());

                                    flock = true;
                                L007:
                                    if (flock)
                                    {
                                        goto L007;
                                    }
                                    else
                                    {

                                        return "file";
                                    }
                                }
                                else
                                {
                                    return "null param accept!";
                                }
                            }
                            else {
                                return __msg_notlogin;
                            }
                        }
                    case "exec":
                        {
                            if (__login())
                            {
                                System.Diagnostics.Process.Start(param);
                                return "Exec OK!";
                            }
                            else {
                                return __msg_notlogin;
                            }
                        }
                    default:
                        {
                            return "This command not found in lib!";
                        }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return "NULL command!";
            }
        }

        #region interface
        public static bool __login() {

            try
            {
                string[] logininf = Decrypt(get_text_file("session.sys")).Split(' ');
                string[] remip = s.RemoteEndPoint.ToString().Split(':');
                string intime = logininf[1].Replace("_", " ");
                DateTime inptime = DateTime.Parse(intime);
                TimeSpan expart = DateTime.Now - inptime;
                Console.WriteLine(logininf[0]);
                if (expart.Minutes > 30 && remip[0] != logininf[0])
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        //public static void HandleIncomingFile(int port)
        public static void HandleIncomingFile()
        {
            flock = true;
            try
            {
                /*
                TcpListener tcpListener = new TcpListener(port);
                tcpListener.Start();
                */
                while (true)
                {
                    //Socket handlerSocket = tcpListener.AcceptSocket();
                    Socket handlerSocket = newList.AcceptSocket();
                    //if (handlerSocket.Connected)
                    if (s.Connected)
                    {
                        string fileName = string.Empty;
                        //NetworkStream networkStream = new NetworkStream(handlerSocket);
                        NetworkStream networkStream = new NetworkStream(s);
                        int thisRead = 0;
                        int blockSize = 1024;
                        Byte[] dataByte = new Byte[blockSize];
                        /*lock (this)
                        {*/
                        string folderPath = @"c:\";
                        //handlerSocket.Receive(dataByte);
                        s.Receive(dataByte);
                        int fileNameLen = BitConverter.ToInt32(dataByte, 0);
                        fileName = Encoding.ASCII.GetString(dataByte, 4, fileNameLen);
                        Stream fileStream = File.OpenWrite(folderPath + fileName);
                        fileStream.Write(dataByte, 4 + fileNameLen, (1024 - (4 + fileNameLen)));
                        while (true)
                        {

                            thisRead = networkStream.Read(dataByte, 0, blockSize);
                            fileStream.Write(dataByte, 0, thisRead);
                            if (thisRead == 0)
                            {
                                Console.WriteLine("File tranff success!");
                                flock = false;
                                break;
                            }
                        }
                        fileStream.Close();

                        
                        /*}
                        if (NewFileRecieved != null)
                        {
                            NewFileRecieved(this, fileName);
                        }*/
                        handlerSocket = null;
                        StreamWriter strwrite = new StreamWriter(networkStream);
                        strwrite.WriteLine("File tranff success!");
                        strwrite.Flush();
                        flock = false;
                    }
                }

            }
            catch
            {

            }
        }

        #endregion

        #region Diagam base

        public static string ExecuteCommandSync(object command)
        {
            string avm = "NULL";
            try
            {
                ProcessStartInfo start = new ProcessStartInfo("cmd", "/c " + command);
                //start.FileName = @"C:\7za.exe"; // Specify exe name.
                start.UseShellExecute = false;
                start.RedirectStandardOutput = true;

                using (Process process = Process.Start(start))
                {
                    using (StreamReader reader = process.StandardOutput)
                    {
                        string result = reader.ReadToEnd();
                        avm = result;
                    }
                }
                return avm;
            }
            catch (Exception ex)
            {
                return "Run CMD -> " + ex.Message;
            }
        }

        public static string LocalIPAddress()
        {
            IPHostEntry host;
            string localIP = "";
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIP = ip.ToString();
                    break;
                }
            }
            return localIP;
        }

        public static string Encrypt(string clearText)
        {
            string EncryptionKey = "MAKV2SPBNI99212**@#^%^^&^$$^&&$#&(*&^#$$&*($&%*&($";
            byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new
                    Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(clearBytes, 0, clearBytes.Length);
                        cs.Close();
                    }
                    clearText = Convert.ToBase64String(ms.ToArray());
                }
            }
            return clearText;
        }

        public static string Decrypt(string cipherText)
        {
            string EncryptionKey = "MAKV2SPBNI99212**@#^%^^&^$$^&&$#&(*&^#$$&*($&%*&($";
            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new
                    Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(cipherBytes, 0, cipherBytes.Length);
                        cs.Close();
                    }
                    cipherText = Encoding.Unicode.GetString(ms.ToArray());
                }
            }
            return cipherText;
        }

        public static void writetext(string path, string lines)
        {
            try
            {
                TextWriter tsw = new StreamWriter(path);

                tsw.WriteLine(lines);
                tsw.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public static string get_text_file(string path)
        {
            try
            {
                using (TextReader reader = File.OpenText(path))
                {
                    string line = reader.ReadToEnd();
                    reader.Close();
                    return line;
                }
            }
            catch (Exception ex)
            {

                return "";
            }
        }

        public static string GetIP()
        {
            IPHostEntry host;
            string localIP = "";
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily.ToString() == "InterNetwork")
                {
                    return  ip.ToString();
                }
            }
            return localIP;
        }

        #endregion
    }
}
